﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using GTServices.WPF.Client.Data;
using GTServices.WPF.Client.ViewModels;
using GTServices.WPF.Client.ViewModels.Entities;
using GTServices.Entities;
using GTServices.Model;
using Caliburn.Micro;
using System.Reflection;


namespace GTServices.WPF.Client
{
    /// <summary>
    /// This class stays in sync with the datamodel. Makes things easier when i'm dealing with the types in the UI. One day i need to abstract this as it's a great model for DB backed applications for persistance 
    /// </summary>
    public class ModelViewModels : PropertyChangedBase
    {
        #region Singleton
        private static ModelViewModels _instance;

        public static ModelViewModels Instance
        {
            get
            {
                lock (_lok)
                {
                    if (_instance == null)
                    {
                        _instance = new ModelViewModels();
                    }
                }
                return _instance;
            }
        }

        private static object _lok = new object();
        #endregion

        //Core Tables
        public SyncedBindableCollection<Category, CategoryViewModel>                   CategoryViewModels { get; set; }
        public SyncedBindableCollection<Pattern, PatternViewModel>               PatternViewModels { get; set; }
        public SyncedBindableCollection<FileExtension, FileExtensionViewModel>   FileExtensionViewModels { get; set; }
        public SyncedBindableCollection<Language, LanguageViewModel>             LanguageViewModels { get; set; }
        public SyncedBindableCollection<Template, TemplateViewModel>             TemplateViewModels { get; set; }
        public SyncedBindableCollection<Classification, ClassificationViewModel> ClassficationViewModels { get; set; }

        public SyncedBindableCollection<Entities.FileInfo, FileInfoViewModel> FileInfoViewModels { get; set; }

        //Result Tables
        public SyncedBindableCollection<MatchInfo, MatchInfoViewModel> MatchInfoViewModels { get; set; }
        public SyncedBindableCollection<GrepRequest, GrepRequestViewModel> GrepRequestViewModels { get; set; }
        public SyncedBindableCollection<GrepResult, GrepResultViewModel> GrepResultViewModels { get; set; }


        private ModelViewModels()
        {

            GTApplication.Instance.DataModel.OnRsync += new Model.Models.DataModel.Resync(DataModel_OnRsync);
            SyncToSource(); 
        }

        void DataModel_OnRsync(Model.Models.DataModel context)
        {
            SyncToSource(); 
        }

        public void SyncToSource()
        {

            CategoryViewModels = new SyncedBindableCollection<Category, CategoryViewModel>(GTApplication.Instance.DataModel.Categories,
                                                                        (x) => new CategoryViewModel() { Category = x },
                                                                        (m, vm) => vm.Category == m);

            NotifyOfPropertyChange(() => CategoryViewModels); 

            PatternViewModels = new SyncedBindableCollection<Pattern, PatternViewModel>(GTApplication.Instance.DataModel.Patterns,
                                                                        (x) => new PatternViewModel() { Pattern = x },
                                                                        (m, vm) => vm.Pattern == m);
            NotifyOfPropertyChange(() => PatternViewModels); 

            FileExtensionViewModels = new SyncedBindableCollection<FileExtension, FileExtensionViewModel>(GTApplication.Instance.DataModel.FileExtensions,
                                                                        (x) => new FileExtensionViewModel() { FileExtension = x },
                                                                        (m, vm) => vm.FileExtension == m);

            NotifyOfPropertyChange(() => FileExtensionViewModels); 
            FileInfoViewModels = new SyncedBindableCollection<FileInfo, FileInfoViewModel>(GTApplication.Instance.DataModel.FileInfos,
                                                                        (x) => new FileInfoViewModel() { FileInfo = x },
                                                                        (m, vm) => vm.FileInfo == m);
            NotifyOfPropertyChange(() => FileInfoViewModels); 
            LanguageViewModels = new SyncedBindableCollection<Language, LanguageViewModel>(GTApplication.Instance.DataModel.Languages,
                                                                        (x) => new LanguageViewModel() { Language = x },
                                                                        (m, vm) => vm.Language == m);
            NotifyOfPropertyChange(() => LanguageViewModels); 
            TemplateViewModels = new SyncedBindableCollection<Template, TemplateViewModel>(GTApplication.Instance.DataModel.Templates,
                                                                                           x => new TemplateViewModel() { Template = x },
                                                                                           (m, vm) => vm.Template == m);
            NotifyOfPropertyChange(() => TemplateViewModels); 
            ClassficationViewModels = new SyncedBindableCollection<Classification, ClassificationViewModel>(GTApplication.Instance.DataModel.Classifications,
                                                                                                            x => new ClassificationViewModel() { Classification = x },
                                                                                                            (m, vm) => vm.Classification == m);


            NotifyOfPropertyChange(() => ClassficationViewModels); 
            GrepResultViewModels = new SyncedBindableCollection<GrepResult, GrepResultViewModel>(GTApplication.Instance.DataModel.GrepResults,
                                                                                                (x) => new GrepResultViewModel() { GrepResult = x },
                                                                                                (m, vm) => vm.GrepResult == m);
            NotifyOfPropertyChange(() => GrepResultViewModels);

            
            GrepRequestViewModels = new SyncedBindableCollection<GrepRequest, GrepRequestViewModel>(GTApplication.Instance.DataModel.GrepRequests,
                                                                                                (x) => new GrepRequestViewModel() { GrepRequest = x },
                                                                                                (m, vm) => vm.GrepRequest == m);
            NotifyOfPropertyChange(() => GrepRequestViewModels);



            FileInfoViewModels = new SyncedBindableCollection<FileInfo, FileInfoViewModel>(GTApplication.Instance.DataModel.FileInfos,
                                                                                                (x) => new FileInfoViewModel() { FileInfo = x },
                                                                                                (m, vm) => vm.FileInfo == m);
            NotifyOfPropertyChange(() => FileInfoViewModels);


            MatchInfoViewModels = new SyncedBindableCollection<MatchInfo, MatchInfoViewModel>(GTApplication.Instance.DataModel.MatchInfos,
                                                                                               (x) => new MatchInfoViewModel() { MatchInfo = x },
                                                                                               (m, vm) => vm.MatchInfo == m);
            NotifyOfPropertyChange(() => MatchInfoViewModels); 

        }

        private ISyncedCollection GetCollectionFor(Type t)
        {
            PropertyInfo[] infos = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (PropertyInfo info in infos)
            {
                if (info.PropertyType.IsGenericType &&
                    info.PropertyType.GetInterfaces().Contains(typeof(ISyncedCollection)) &&
                    info.PropertyType.GetGenericArguments().Length > 0 &&
                    info.PropertyType.GetGenericArguments()[0] == t)
                {
                    object o = info.GetValue(this, null);
                    if (o != null)
                        return o as ISyncedCollection;
                }
            }
            return null;
        }

        private IList GetCollectionFor(PropertyChangedBase viewModel)
        {
            PropertyInfo[] infos = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (PropertyInfo info in infos)
            {
                if (info.PropertyType.IsGenericType &&
                    info.PropertyType.GetInterfaces().Contains(typeof(IList)) &&
                    info.PropertyType.GetGenericArguments().Length > 0 &&
                    info.PropertyType.GetGenericArguments()[1] == viewModel.GetType())
                {
                    object o = info.GetValue(this, null);
                    if (o != null)
                        return o as IList;
                }
            }
            return null;
        }


        public PropertyChangedBase TryGetViewModelFrom(Object o)
        {
            ISyncedCollection collection = GetCollectionFor(o.GetType());
            return (PropertyChangedBase)collection.GetSyncedItemFromSource(o);
        }

        public bool TryRemoveViewModel(PropertyChangedBase pcb)
        {
            IList list = GetCollectionFor(pcb);
            if (list != null)
            {
                list.Remove(pcb);
                return true; 
            }
            return false;
        }
    }
}
